<?php


namespace Tools;


/**
 * 数组工具
 * Class ArrayTool
 *
 * @package Tools
 */
class ArrayTool
{

    /**
     * 查询 pid 所有子集
     *
     * @param array  $list
     * @param mixed  $pid
     * @param string $id_name
     * @param string $pid_name
     *
     * @return array
     */
    public static function arrayFindChild(array $list, $pid = 0, string $id_name = 'id', string $pid_name = 'pid'): array
    {
        $map = [];
        foreach ($list as $element) {
            $element                          = (array)$element;
            $map[$element[$pid_name] ?? ''][] = $element[$id_name];
        }

        $result = [];
        $stack  = [$pid];
        while ($stack) {
            $current = array_shift($stack);
            if (isset($map[$current])) {
                foreach ($map[$current] as $id) {
                    $result[] = $id;
                    $stack[]  = $id;
                }
            }
        }
        return $result;
    }

    /**
     * 查找数组所有的父集
     *
     * @param array      $list
     * @param int|string $id
     * @param string     $id_name
     * @param string     $pid_name
     *
     * @return array
     */
    public function array_find_parent(array $list, $id = 0, $id_name = 'id', $pid_name = 'pid'): array
    {
        $map = [];
        foreach ($list as $element) {
            $element                         = (array)$element;
            $map[$element[$id_name] ?? ''][] = $element[$pid_name];
        }

        // 迭代遍历
        $result = [];
        $stack  = [$id];
        while ($stack) {
            $current = array_shift($stack);
            if (isset($map[$current])) {
                foreach ($map[$current] as $pid) {
                    $result[] = $pid;
                    $stack[]  = $pid;
                }
            }
        }

        return $result;
    }

    /**
     * 将大数组转换为数组树
     *
     * @param array  $list
     * @param        $pid
     * @param string $id_name
     * @param string $pid_name
     *
     * @return array
     */
    public static function arrayToTree(array $list, string $id_name = 'id', string $pid_name = 'pid'): array
    {

        if (!$list) {
            return $list;
        }

        $pid = min(array_column($list, $pid_name));

        $childrenMap = [];
        foreach ($list as &$item) {
            $item                                  = (array)$item;
            $childrenMap[$item[$pid_name] ?? -1][] = &$item;
        }
        unset($item);

        $builder = function ($currentPid) use (&$builder, &$childrenMap, $id_name) {
            $branch = [];
            foreach ($childrenMap[$currentPid] ?? [] as $node) {
                $node['isParent'] = false;
                if ($childNodes = $builder($node[$id_name])) {
                    $node['isParent'] = true;
                    $node['children'] = $childNodes;
                }
                $branch[] = $node;
            }
            return $branch;
        };

        return $builder($pid);
    }


    /**
     * 将大数组转换为数组树
     *
     * @param array  $list
     * @param        $root_id
     * @param string $id_key
     * @param string $parent_key
     *
     * @return array
     * @throws \Exception
     */
    public static function arrayBigToTree(array $list, $root_id = 0, string $id_key = 'id', string $parent_key = 'pid'): array
    {
        $map    = [];
        $id_set = [];

        foreach ($list as &$item) {
            $item = (array)$item;
            $id   = $item[$id_key] ?? null;
            $pid  = $item[$parent_key] ?? -1;

            // 数据校验
            if ($id === null || !is_scalar($id)) {
                throw new \Exception("无效的节点ID: " . var_export($id, true));
            }

            // 循环引用检测
            if (isset($id_set[$id])) {
                throw new \Exception("检测到重复ID或循环引用: {$id}");
            }
            $id_set[$id] = true;

            $map[$pid][] = &$item;
        }
        unset($item); // 及时释放引用

        // 迭代构建树（防堆栈溢出）
        $tree    = [];
        $stack   = [[$root_id, &$tree]];
        $visited = []; // 深度监控

        while (!empty($stack)) {
            [$current_id, &$branch] = array_pop($stack);

            // 深度保护（可选）
            if (count($visited) > 1000000) {
                throw new \Exception("超过最大允许深度（100万层）");
            }
            $visited[$current_id] = true;

            foreach ($map[$current_id] ?? [] as &$node) {
                $node['children'] = [];
                $stack[]          = [$node[$id_key], &$node['children']];
                $branch[]         = $node;
            }
        }

        return $tree;
    }

    /**
     * 将树转换为一维数组
     *
     * @param array  $array
     * @param int    $level
     * @param string $id_key
     * @param string $name_key
     * @param string $indent
     *
     * @return \Generator
     */
    public static function treeToArray(array $array, int $level = 0, string $id_key = 'id', string $name_key = 'name', string $indent_prefix = '‎', string $indent = "&emsp;")
    {
        foreach ($array as $node) {
            if (!empty($node[$name_key])) {
                yield [
                    $id_key   => $node[$id_key] ?? null,
                    $name_key => ($level == 0 ? "" : $indent_prefix) . str_repeat($indent, $level) . $node[$name_key],
                ];
            }

            if (!empty($node['children'])) {
                yield from self::treeToArray($node['children'], $level + 1, $id_key, $name_key, $indent_prefix, $indent);
            }
        }
    }


}